
#include "WarGameLib.h"


#include <SDL_image.h>







wgHeightmap::wgHeightmap(void)
{
	data = 0;
	width = height = 0;
	setSampleSize(1, 1, 1);
}

wgHeightmap::~wgHeightmap()
{
	if(data != 0)
	{
		delete[] data;
		data = 0;
	}
	for(vector<wgHeightmapTexture*>::iterator it = textures.begin(); it != textures.end(); ++it)
		delete *it;

	DestroySubPatches(&rootPatch);
}

bool wgHeightmap::Load(string& filename)
{

	wgImage image;
	cout << "Loading heightmap from " << filename << "...";

	string imagePath = filename;
	imagePath.append("heightmap.png");
	if(! image.Load(imagePath.c_str()))
	{
		cout << IMG_GetError() << endl;
		return false;
	}
	cout << "OK" << endl;


	LoadTextures(filename);


	width = image.getWidth();
	height = image.getHeight();

	int iSize = width * height;
	data = new wgHeightmapData[iSize];



	int r,g,b;

	for(int i = 0; i < iSize; ++i)
	{
		image.getPixel(i, &r, &g, &b);
		data[i].value = r;
		int value = r;

		

		
	}






	CalculateVertexNormals();

	DividePatches();


	return true;
}
void wgHeightmap::LoadTextures(string& filename)
{
	cout << "Loading heightmap textures..." << endl;
	string descFilename = filename + "hm_tex.txt";

	FILE* file;
	fopen_s(&file, descFilename.c_str(), "r");

	if(file == 0)
	{
		cout << "Texture description file not found!" << endl;
		return;
	}


	stringstream ss;
	wgHeightmapTexture *tex;

	while(!feof(file))
	{


		
		ss.str("");
		tex = 0;

		int min, opt;
		char texname[20];
		char buff[80];
		fgets(buff, 80, file);
		sscanf_s(buff, "%d %d",  &min, &opt);
		fgets(texname, 20, file);
		int len = strlen(texname);
		if(texname[len-1] == '\n')
			texname[len-1]='\0';

		cout << "\tMin = " << min << " Optimal = " << opt << endl;

		ss << filename << texname;

		cout << "\tLoading texture \"" << ss.str() << "\"..." << endl;
		tex = new wgHeightmapTexture;

		memset(tex, 0, sizeof(tex));


		if(!tex->image.Load(ss.str().c_str()))
		{
			cout << IMG_GetError() << endl;
			delete tex;
			continue;
		}
		tex->image.CreateTexture();
		tex->min = min;
		tex->optimal = opt;
		tex->max = opt + opt - min;
		textures.push_back(tex);

	}

	fclose(file);
}



void wgHeightmap::CalculateVertexNormals()
{
	for(int x = 0; x < width-1; ++x)
		for(int z = 0; z < height-1; ++z)
		{

			wgHeightmapData* dataPtr = getDataPtr(x,z);
			float a = sampleY * dataPtr->value;
			float b = sampleY * getHeightValue(x+1,z);
			float c = sampleY * getHeightValue(x,z+1);

			
			vector3_t normal;
			normal.x = a - b;
			normal.y = 1.0f;
			normal.z = a - c;
			normalize(&normal);
			dataPtr->normal = normal;

			

		}
}


int wgHeightmap::getHeight() { return height;}
int wgHeightmap::getWidth() { return width;}
void wgHeightmap::setSampleSize(float sx, float sy, float sz)
{
	sampleX = sx;
	sampleY = sy;
	sampleZ = sz;
}

float wgHeightmap::getMapSizeX()
{
	return getWidth() * sampleX;
}
float wgHeightmap::getMapSizeY()
{
	return 255 * sampleZ;
}
float wgHeightmap::getMapSizeZ()
{
	return getHeight() * sampleY;
}

int wgHeightmap::getHeightValue(int x, int y)
{
	
	return getDataPtr(x,y)->value;
}
float wgHeightmap::getSampledHeight(float x, float z)
{
	int fX1 = (int) floorf(x / sampleX);
	int fX2 = (int) ceilf(x / sampleX);
	int fZ1 = (int) floorf(z / sampleZ);
	int fZ2 = (int) ceilf(z / sampleZ);

	float value = (getHeightValue(fX1, fZ1) + getHeightValue(fX2, fZ1) + getHeightValue(fX1, fZ2) + getHeightValue(fX2, fZ2)) / 4.0f;
	return value * sampleY;
}


wgHeightmapData* wgHeightmap::getDataPtr(int x, int y)
{
	if(x >= width)
		x = 0;
	if(y >= height)
		y = 0;
	return &data[(y * width) + x];
}


void wgHeightmap::Render()
{

	glColor3ub(255, 255, 255);


	textures[0]->image.UseTexture();


	glEnable(GL_TEXTURE_2D);
	
	glDisable(GL_TEXTURE_2D);

	RenderPatch(&rootPatch);


	glDisable(GL_TEXTURE_2D);



}

void wgHeightmap::RenderPatch(wgHeightmapPatch *p)
{
	if(p == 0 || !p->visible)
		return;

	if(p->subPatches[0] == 0)
		Render(p->x, p->z, p->width, p->height);
	else
	{
		RenderPatch(p->subPatches[0]);
		RenderPatch(p->subPatches[1]);
		RenderPatch(p->subPatches[2]);
		RenderPatch(p->subPatches[3]);
	}
	
}


void wgHeightmap::Render(int x, int z, int sX, int sZ)
{



	
	for(int iZ = z; iZ < z + sZ; ++iZ)
	{
		
		//getDataPtr(0,iZ)->textures[0]->UseTexture();
	
		//glBegin(GL_TRIANGLE_STRIP);

		




		for(int iX = x; iX < x + sX; ++iX)
		{
			wgHeightmapData* dataPtr = getDataPtr(iX,iZ);
			//if(dataPtr->visible)
			{
				glBegin(GL_TRIANGLE_STRIP);
				DrawHeightmapVertex(iX, iZ);
				DrawHeightmapVertex(iX, iZ+1);
				DrawHeightmapVertex(iX+1, iZ);
				DrawHeightmapVertex(iX+1, iZ+1);
				glEnd();
			}
			
			
		}
		/*glEnd();*/

	}
	
}



void wgHeightmap::DrawHeightmapVertex(int x, int z)
{
	wgHeightmapData* dataPtr = getDataPtr(x,z);
	int value = dataPtr->value;



	float fX = x * sampleX;
	float fY = value * sampleY;
	float fZ = z * sampleZ;


	//cout << fX << ", " << fY << ", " << fZ << endl;

	
			

	glTexCoord2f( sampleX * x / (float)width,  sampleZ * z / (float)height);
	
	//glColor3ub(value, value, value);
	glNormal3fv(&dataPtr->normal.x);
	glVertex3f(fX, fY, fZ);
			
}

void wgHeightmap::BindTextures()
{
	if(textures.empty())
		glBindTexture(GL_TEXTURE_2D, 0);
	else
		textures[0]->image.UseTexture();

}
/*

void wgHeightmap::Update(wgCamera& c)
{

	float offX = sampleX * .5f;
	float offZ = sampleZ * .5f;
	float offY = 255.0f * sampleY;

	wgHeightmapData* pData = data;
	for(int z = 0; z < getHeight(); ++z)
		for(int x = 0; x < getWidth(); ++x)
		{
			pData->visible = c.SimpleFrustumBoxTest(x + offX, 0, z + offZ, sampleX, offY, sampleZ);
			pData++;
		}
}*/

void wgHeightmap::Update(wgCamera &c)
{
	UpdatePatches(c, &rootPatch);

}

void wgHeightmap::UpdatePatches(wgCamera &cam, wgHeightmapPatch *pPatch)
{
	float offX = sampleX * .5f;
	float offZ = sampleZ * .5f;
	float offY = 255.0f * sampleY;

	float fX = pPatch->x * sampleX;
	float fY = getHeightValue(pPatch->x, pPatch->z) * sampleY;
	float fZ = pPatch->z * sampleZ;

	pPatch->visible = cam.SimpleFrustumBoxTest(fX + offX, 0, fZ + offZ, pPatch->width * sampleX, offY, pPatch->height * sampleZ);
	if(!pPatch->visible)
		return;
	if(pPatch->subPatches[0] != 0)
	{
		UpdatePatches(cam, pPatch->subPatches[0]);
		UpdatePatches(cam, pPatch->subPatches[1]);
		UpdatePatches(cam, pPatch->subPatches[2]);
		UpdatePatches(cam, pPatch->subPatches[3]);
	}
}


void wgHeightmap::DivideHeightmapPatch(int x, int y, int w, int h, wgHeightmapPatch *pPatch)
{
	memset(pPatch, 0, sizeof(wgHeightmapPatch));
	pPatch->x = x;
	pPatch->z = y;
	pPatch->width = w;
	pPatch->height = h;

	if(w > HEIGHTMAP_PATCH_SIZE || h > HEIGHTMAP_PATCH_SIZE)
	{
		pPatch->subPatches[0] = new wgHeightmapPatch;
		DivideHeightmapPatch(x, y, w / 2, h / 2, pPatch->subPatches[0]);
		
		pPatch->subPatches[1] = new wgHeightmapPatch;
		DivideHeightmapPatch(x + w / 2, y, w / 2, h / 2, pPatch->subPatches[1]);
		
		pPatch->subPatches[2] = new wgHeightmapPatch;
		DivideHeightmapPatch(x, y + h / 2, w / 2, h / 2, pPatch->subPatches[2]);
		
		pPatch->subPatches[3] = new wgHeightmapPatch;
		DivideHeightmapPatch(x + w / 2, y + h / 2, w / 2, h / 2, pPatch->subPatches[3]);
	}
	else
	{

	}

}

void wgHeightmap::DividePatches()
{

	DivideHeightmapPatch(0, 0, getWidth(), getHeight(), &rootPatch);
}

void wgHeightmap::DestroySubPatches(wgHeightmapPatch*p)
{
	for(int i = 0; i < 4; ++i)
		if(p->subPatches[i] != 0)
		{
			DestroySubPatches(p->subPatches[i]);
			delete p->subPatches[i];
		}
}